﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using OPCAutomation;

namespace OPCDemo
{
    public partial class FrmMain : Form
    {
        public FrmMain()
        {
            InitializeComponent();

            updateTimer.Interval = 500;
            updateTimer.Tick += UpdateTimer_Tick;

            this.cmb_ReadWay.DataSource = Enum.GetValues(typeof(ReadMethod));

            this.Load += FrmMain_Load;
        }

        private void FrmMain_Load(object sender, EventArgs e)
        {
            Task.Run(() =>
            {
                IPHostEntry iPHost = Dns.GetHostEntry(Environment.MachineName);

                if (iPHost.AddressList.Length > 0)
                {
                    for (int i = 0; i < iPHost.AddressList.Length; i++)
                    {
                        string host = Dns.GetHostEntry(iPHost.AddressList[i]).HostName;

                        this.Invoke(new Action(() =>
                        {
                            if (!this.cmb_ServerNode.Items.Contains(host))
                            {
                                this.cmb_ServerNode.Items.Add(host);
                            }
                        }));
                    }
                }

                this.Invoke(new Action(() =>
                {
                    if (this.cmb_ServerNode.Items.Count > 0)
                    {
                        this.cmb_ServerNode.SelectedIndex = 0;
                    }
                }));
            }); 
        }

        //OPCServer => OPCGroups =>OPCGroup =>OPCItems => OPCItem

        private OPCServer OPCServer;

        private OPCGroups OPCGroups;

        private OPCGroup OPCGroup;

        private List<OPCDAVariable> OPCVarList = new List<OPCDAVariable>();

        private Array ServerHandles;

        private Array Errors;

        private Timer updateTimer = new Timer();

        private ReadMethod readMethod;


        /// <summary>
        /// 列举计算机名称
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Refresh_Click(object sender, EventArgs e)
        {
            // 清空节点列表
            this.cmb_ServerNode.Items.Clear();

            IPHostEntry iPHost = Dns.GetHostEntry(Environment.MachineName);

            if(iPHost.AddressList.Length > 0 )
            {
                for (int i = 0; i < iPHost.AddressList.Length; i++)
                {
                    string host = Dns.GetHostEntry(iPHost.AddressList[i]).HostName;

                    if(!this.cmb_ServerNode.Items.Contains(host) )
                    {
                        this.cmb_ServerNode.Items.Add(host);
                    }
                }
            }
            if(this.cmb_ServerNode.Items.Count > 0)
            {
                this.cmb_ServerNode.SelectedIndex = 0;
            }
        }

        /// <summary>
        /// 列举所有的OPC服务名称
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cmb_ServerNode_SelectedIndexChanged(object sender, EventArgs e)
        {
            if(OPCServer == null)
            {
                OPCServer = new OPCServer();
            }

            object serverList = OPCServer.GetOPCServers(this.cmb_ServerName.Text);

            foreach (var item in (Array)serverList)
            {
                if (!this.cmb_ServerName.Items.Contains(item))
                {
                    this.cmb_ServerName.Items.Add(item);
                }
            }
            if(this.cmb_ServerName.Items.Count > 0)
            {
                this.cmb_ServerName.SelectedIndex = 1;
            }
        }

        private void btn_Connect_Click(object sender, EventArgs e)
        {
            if(this.btn_Connect.Text == "连接")
            {
                //实现连接的代码
                if(OPCServer != null)
                {
                    OPCServer = new OPCServer();
                }

                try
                {
                    OPCServer.Connect(this.cmb_ServerName.Text.Trim(), this.cmb_ServerNode.Text.Trim());
                }
                catch (Exception ex)
                {
                    MessageBox.Show("连接失败：" + ex.Message);
                    return;
                }

                this.btn_Connect.Text = "断开";

                //获取变量列表
                this.lst_Items.Items.Clear();

                OPCBrowser opcBrowser = OPCServer.CreateBrowser();
                opcBrowser.ShowBranches();
                opcBrowser.ShowLeafs(true);

                foreach(var item in opcBrowser)
                {
                    this.lst_Items.Items.Add(item);
                }

                //创建OPCGROUPS对象
                OPCGroups = OPCServer.OPCGroups;
                OPCGroups.DefaultGroupDeadband = 0;
                OPCGroups.DefaultGroupIsActive = true;

                //创建OPCGROUP对象
                OPCGroup = OPCGroups.Add("Thinger");
                OPCGroup.IsActive = true;
                OPCGroup.UpdateRate = 200;
                OPCGroup.IsSubscribed = true;
            }
            else
            {
                if(this.updateTimer.Enabled == true)
                {
                    this.updateTimer.Enabled = false;
                }
                //实现断开的代码
                if(OPCServer != null)
                {
                    OPCServer.Disconnect();
                    this.btn_Connect.Text = "连接";
                }             
            }
        }

        private void lst_Items_DoubleClick(object sender, EventArgs e)
        {
            if(this.lst_Items.SelectedItems != null)
            {
                string itemId = this.lst_Items.SelectedItem.ToString();

                var variable = this.OPCVarList.Find(c => c.ItemId == itemId);

                if(variable != null )
                {
                    MessageBox.Show("该变量名称已经存在", "添加变量");
                    return;
                }

                this.OPCVarList.Add(new OPCDAVariable()
                {
                    ItemId = itemId,
                    ClientHandle = this.OPCVarList.Count
                });

                int rowIndex = this.dgv_Data.Rows.Add();
                this.dgv_Data.Rows[rowIndex].Cells[0].Value = this.OPCVarList[rowIndex].ItemId;
                this.dgv_Data.Rows[rowIndex].Cells[1].Value = this.OPCVarList[rowIndex].ClientHandle;

                //清除OPCItems
                if(OPCGroup.OPCItems.Count > 0 )
                {
                    OPCGroup.OPCItems.Remove(this.OPCGroup.OPCItems.Count, ref ServerHandles, out Errors);
                }

                List<string> itemIdsTemp = new List<string>();
                List<int> clientHandlesTemp = new List<int>();

                //占位
                itemIdsTemp.Add("");
                clientHandlesTemp.Add(0);

                for(int i = 0; i < this.OPCVarList.Count; i++)
                {
                    itemIdsTemp.Add(this.OPCVarList[i].ItemId);
                    clientHandlesTemp.Add(this.OPCVarList[i].ClientHandle);
                }

                OPCGroup.OPCItems.AddItems(this.OPCVarList.Count, itemIdsTemp.ToArray(),clientHandlesTemp.ToArray(),out ServerHandles, out Errors);
            }
        }

        private void UpdateTimer_Tick(object sender, EventArgs e)
        {
            if(this.OPCVarList.Count > 0)
            {
                switch (readMethod)
                {
                    case ReadMethod.同步:

                        try
                        {
                            OPCGroup.SyncRead(1, this.OPCVarList.Count, ref ServerHandles, out Array Values, out Errors, out object Qualities, out object TimeStamps);

                            //解析
                            for(int i = 0; i < this.OPCVarList.Count;i++)
                            {
                                object value = Values.GetValue(i + 1);

                                if(value != null)
                                {
                                    this.dgv_Data.Rows[i].Cells[2].Value = value.ToString();
                                    this.dgv_Data.Rows[i].Cells[3].Value = value.GetType().ToString();
                                    this.dgv_Data.Rows[i].Cells[4].Value = ((Array)Qualities).GetValue(i+1).ToString();
                                    this.dgv_Data.Rows[i].Cells[5].Value = ((Array)TimeStamps).GetValue(i + 1).ToString();
                                }
                            }

                        }
                        catch (Exception ex)
                        {                        
                            this.updateTimer.Enabled = false;
                            this.btn_Read.Text = "开始读取";
                            MessageBox.Show("同步读取出错:" + ex.Message, "同步读取");
                        }

                        break;
                    case ReadMethod.异步:

                        try
                        {
                            OPCGroup.AsyncRead(this.OPCVarList.Count, ref ServerHandles, out Errors, 1, out int CancelId);
                        }
                        catch (Exception ex)
                        {                          
                            this.updateTimer.Enabled = false;
                            this.btn_Read.Text = "开始读取";
                            MessageBox.Show("异步读取出错:" + ex.Message, "异步读取");
                        }

                        break;
                    case ReadMethod.订阅:
                        break;
                    default:
                        break;
                }
            }
        }

        private void btn_Read_Click(object sender, EventArgs e)
        {
            if(this.btn_Read.Text == "开始读取")
            {
                //执行开始读取
                readMethod = (ReadMethod)Enum.Parse(typeof(ReadMethod),this.cmb_ReadWay.Text, true);

                switch (readMethod)
                {
                    case ReadMethod.同步:
                        this.updateTimer.Enabled = true;
                        break;
                    case ReadMethod.异步:
                        OPCGroup.AsyncReadComplete += OPCGroup_AsyncReadComplete;
                        this.updateTimer.Enabled = true;
                        break;
                    case ReadMethod.订阅:
                        OPCGroup.DataChange += OPCGroup_DataChange;
                        OPCGroup.IsSubscribed = true;
                        break;
                    default:
                        break;
                }

                this.btn_Read.Text = "停止读取";
            }
            else
            {
                readMethod = (ReadMethod)Enum.Parse(typeof(ReadMethod), this.cmb_ReadWay.Text, true);

                switch (readMethod)
                {
                    case ReadMethod.同步:
                        this.updateTimer.Enabled = false;
                        break;
                    case ReadMethod.异步:
                        OPCGroup.AsyncReadComplete -= OPCGroup_AsyncReadComplete;
                        this.updateTimer.Enabled = false;
                        break;
                    case ReadMethod.订阅:
                        OPCGroup.DataChange -= OPCGroup_DataChange;
                        OPCGroup.IsSubscribed = false;
                        break;
                    default:                   
                        break;
                }

                //执行停止读取
                this.btn_Read.Text = "开始读取";
            }
        }

        private void OPCGroup_DataChange(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps)
        {
            for (int i = 1; i <= NumItems; i++)
            {
                int clientHandle = Convert.ToInt32(ClientHandles.GetValue(i));

                this.dgv_Data.Rows[clientHandle].Cells[2].Value = ItemValues.GetValue(i);
                this.dgv_Data.Rows[clientHandle].Cells[3].Value = ItemValues.GetValue(i).GetType();
                this.dgv_Data.Rows[clientHandle].Cells[4].Value = Qualities.GetValue(i);
                this.dgv_Data.Rows[clientHandle].Cells[5].Value = TimeStamps.GetValue(i);
            }
        }

        private void OPCGroup_AsyncReadComplete(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps, ref Array Errors)
        {
            for (int i = 1; i <= NumItems; i++)
            {
                int clientHandle = Convert.ToInt32(ClientHandles.GetValue(i));

                this.dgv_Data.Rows[clientHandle].Cells[2].Value = ItemValues.GetValue(i);
                this.dgv_Data.Rows[clientHandle].Cells[3].Value = ItemValues.GetValue(i).GetType();
                this.dgv_Data.Rows[clientHandle].Cells[4].Value = Qualities.GetValue(i);
                this.dgv_Data.Rows[clientHandle].Cells[5].Value = TimeStamps.GetValue(i);
            }
        }

        private void dgv_Data_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
        {
            if(this.dgv_Data.SelectedRows.Count > 0)
            {
                //获取到索引
                int index = this.dgv_Data.CurrentRow.Index;

                string itemId = this.dgv_Data.Rows[index].Cells[0].Value.ToString();
                string varType = this.dgv_Data.Rows[index].Cells[3].Value.ToString();
                string current = this.dgv_Data.Rows[index].Cells[2].Value.ToString();

                FrmParamSet frmParamSet = new FrmParamSet(itemId, varType, current);

                DialogResult dialoResult = frmParamSet.ShowDialog();

                int clientHandle = Convert.ToInt32(this.dgv_Data.Rows[index].Cells[1].Value);

                if(dialoResult == DialogResult.OK)
                {
                    string setValue = frmParamSet.SetValue;
                    bool isSync = frmParamSet.IsSync;

                    List<int> tempServerHandle = new List<int>();
                    List<object> tempSetValue = new List<object>();

                    tempServerHandle.Add(0);
                    tempSetValue.Add("");

                    tempServerHandle.Add(Convert.ToInt32(ServerHandles.GetValue(index + 1)));
                    tempSetValue.Add(setValue);

                    Array WriteServerHandle = tempServerHandle.ToArray();
                    Array WriteSetValue = tempSetValue.ToArray();

                    if(isSync)
                    {
                        try
                        {
                            OPCGroup.SyncWrite(1, ref WriteServerHandle, ref WriteSetValue, out Errors);
                        }
                        catch (Exception)
                        {
                            MessageBox.Show("同步写入失败", "同步写入");
                        }
                    }
                    else
                    {
                        try
                        {
                            OPCGroup.AsyncWrite(1, ref WriteServerHandle, ref WriteSetValue, out Errors,1,out int cancelID);
                        }
                        catch (Exception)
                        {
                            MessageBox.Show("异步写入失败", "异步写入");
                        }
                    }
                }
            }
        }
    }
}
